//
//  PrintUtils.swift
//  MZPrinter
//
//  Created by lin tendy on 15/11/4.
//  Copyright © 2015年 Mazing Inc. All rights reserved.
//

//import Foundation
import UIKit

/*
* CodePage table
*/
class CodePage {
    static let PC437: UInt8 = 0
    static let KATAKANA: UInt8 = 1
    static let PC850: UInt8 = 2
    static let PC860: UInt8 = 3
    static let PC863: UInt8 = 4
    static let PC865: UInt8 = 5
    static let WPC1252: UInt8 = 16
    static let PC866: UInt8 = 17
    static let PC852: UInt8 = 18
    static let PC858: UInt8 = 19
}

/**
 * BarCode table
 */
class BarCode {
    static let UPC_A: UInt8 = 0
    static let UPC_E: UInt8 = 1
    static let EAN13: UInt8 = 2
    static let EAN8: UInt8 = 3
    static let CODE39: UInt8 = 4
    static let ITF: UInt8 = 5
    static let NW7: UInt8 = 6
    //static let CODE93      = 72
    //static  let CODE128     = 73
}

class PrinterUtil {
    static let ESC: UInt8 = 27//换码
    static let FS: UInt8 = 28//文本分隔符
    static let GS: UInt8 = 29//组分隔符
    static let DLE: UInt8 = 16//数据连接换码
    static let EOT: UInt8 = 4//传输结束
    static let ENQ: UInt8 = 5//询问字符
    static let SP: UInt8 = 32//空格
    static let HT: UInt8 = 9//横向列表
    static let LF: UInt8 = 10//打印并换行（水平定位）
    static let CR: UInt8 = 13//归位键
    static let FF: UInt8 = 12//走纸控制（打印并回到标准模式（在页模式下） ）
    static let CAN: UInt8 = 24//作废（页模式下取消打印数据 ）
    
    
    
    
    
    /**
    * 初始化打印机
    * Clears the data in the print buffer and resets the printer modes to the modes that were
    * in effect when the power was turned on.
    * ESC @
    *
    * @return bytes for this command
    */
    class func initPrinter() -> [UInt8] {
        var result = [UInt8](count: 2, repeatedValue: 0)
        result[0] = ESC
        result[1] = 64
        return result
    }
    
    /**
     * 打印并换行
     * LF
     *
     * @return bytes for this command
     */
    class func printLineFeed() -> [UInt8] {
        var result = [UInt8](count: 1, repeatedValue: 0)
        result[0] = LF
        return result
    }
    
    /**
     * 下划线
     * ESC - n/FS - n
     *
     * @param cn  是否为中文
     * @param dot 线宽 （0表示关闭）
     * @return bytes for this command
     */
    class func  underLine(cn: Bool, dot: UInt32) -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = cn ? FS : ESC
        result[1] = 45
        switch (dot) {
        case 0:
            result[2] = 0
            break
        case 1:
            result[2] = 1
            break
        case 2:
            result[2] = 2
            break
        default:
            result[2] = 0
        }
        return result
    }

    
    class func turnOnPrinter() -> [UInt8]{
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = ESC
        result[1] = 0x3D
        result[2] = 0x00
        return result
    }
    
    class func chosePrintMode(mode: UInt8) -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = ESC
        result[1] = 21
        result[2] = mode
        return result
    }
    
    /**
     * 开启着重强调(加粗)
     * ESC E n
     *
     * @return bytes for this command
     */
    class func  emphasizedOn() -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = ESC
        result[1] = 69
        result[2] = 0xF
        return result
    }
    
    /**
     * 关闭着重强调(加粗)
     * ESC E n
     *
     * @return bytes for this command
     */
    class func  emphasizedOff() -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = ESC
        result[1] = 69
        result[2] = 0
        return result
    }
    
    class func  overlappingOn() -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = ESC
        result[1] = 47
        result[2] = 0xF
        return result
    }
    
    class func  overlappingOff() -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = ESC
        result[1] = 47
        result[2] = 0
        return result
    }
    
    /**
     * 开启 double-strike 模式
     * ESC G n
     *
     * @return bytes for this command
     */
    class func  doubleStrikeOn() -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = ESC
        result[1] = 71
        result[2] = 0xF
        return result
    }
    
    /**
     * 关闭 double-strike 模式
     * ESC G n
     *
     * @return bytes for this command
     */
    class func  doubleStrikeOff() -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = ESC
        result[1] = 71
        result[2] = 0
        return result
    }
    
    /**
     * Select Font A
     * ESC M n
     *
     * @return bytes for this command
     */
    class func  selectFontA() -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = ESC
        result[1] = 77
        result[2] = 0
        return result
    }
    
    /**
     * Select Font B
     * ESC M n
     *
     * @return bytes for this command
     */
    class func  selectFontB() -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = ESC
        result[1] = 77
        result[2] = 1
        return result
    }
    
    /**
     * Select Font C ( some printers don't have font C )
     * ESC M n
     *
     * @return bytes for this command
     */
    class func  selectFontC() -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = ESC
        result[1] = 77
        result[2] = 2
        return result
    }
    
    /**
     * Select Font A
     * FS ! n
     *
     * @return bytes for this command
     */
    class func  selectCNFontA() -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = FS
        result[1] = 33
        result[2] = 0
        return result
    }
    
    /**
     * Select Font B
     * FS ! n
     *
     * @return bytes for this command
     */
    class func  selectCNFontB() -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = FS
        result[1] = 33
        result[2] = 1
        return result
    }
    
    /**
     * 关闭双倍字高
     * ESC ! n
     *
     * @return bytes for this command
     */
    class func  doubleHeightWidthOff() -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = ESC
        result[1] = 33
        result[2] = 0
        return result
    }
    
    /*
     * 使用更小的字体
     */
    class func smallerFontOn() -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = ESC
        result[1] = 33
        result[2] = 1
        return result
    }
    
    /*
     * 取消使用更小的字体
     */
    class func smallerFontOff() -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = ESC
        result[1] = 33
        result[2] = 0
        return result
    }

    /**
     * 双倍字高（仅英文字体有效）
     * ESC ! n
     *
     * @return bytes for this command
     */
    class func  doubleHeightOn() -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = ESC
        result[1] = 33
        result[2] = 16
        return result
    }
    
    /**
     * 双倍字体高宽（仅英文字体有效）
     * ESC ! n
     *
     * @return bytes for this command
     */
    class func  doubleHeightWidthOn() -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = ESC
        result[1] = 33
        result[2] = 56
        return result
    }
    
    
    /**
     * 左对齐
     * ESC a n
     *
     * @return bytes for this command
     */
    class func  alignLeft()  -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = ESC
        result[1] = 97
        result[2] = 0
        return result
    }
    
    /**
     * 居中对齐
     * ESC a n
     *
     * @return bytes for this command
     */
    class func  alignCenter()  -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = ESC
        result[1] = 97
        result[2] = 1
        return result
    }
    
    /**
     * 右对齐
     * ESC a n
     *
     * @return bytes for this command
     */
    class func  alignRight() -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = ESC
        result[1] = 97
        result[2] = 2
        return result
    }
    
    
    /*
    * 打印并走纸n行
    * Prints the data in the print buffer and feeds n lines
    * ESC d n
    *
    * @param n lines
    * @return bytes for this command
    */
    class func  printAndFeedLines(n: UInt8) -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = ESC
        result[1] = 100
        result[2] = n
        return result
    }
    
    /**
     * 打印并反向走纸n行（不一定有效）
     * Prints the data in the print buffer and feeds n lines in the reserve direction
     * ESC e n
     *
     * @param n lines
     * @return bytes for this command
     */
    class func  printAndReverseFeedLines(n: UInt8) -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = ESC
        result[1] = 101
        result[2] = n
        return result
    }
    
    class func  printHorizontalTab() -> [UInt8] {
        var result = [UInt8](count: 5, repeatedValue: 0)
        result[0] = ESC
        result[1] = 44
        result[2] = 20
        result[3] = 28
        result[4] = 0
        return result
    }
    
    class func  printHTNext() -> [UInt8] {
        var result = [UInt8](count: 1, repeatedValue: 0)
        result[0] = HT
        return result
    }
    
    // 默认的行间距
    class func  printLineNormalHeight() -> [UInt8] {
        var result = [UInt8](count: 2, repeatedValue: 0)
        result[0] = ESC
        result[1] = 50
        return result
    }
    
    // 设置行间距
    class func  printLineHeight(height: UInt8) -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = ESC
        result[1] = 51
        result[2] = height
        return result
    }
    
    /**
     * Select character code table
     * ESC t n
     *
     * @param cp example:CodePage.WPC1252
     * @return bytes for this command
     */
    class func  selectCodeTab(cp: UInt8) -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = ESC
        result[1] = 116
        result[2] = cp
        return result
    }
    
    /**
     * 弹开纸箱
     * Drawer kick-out connector pin 2
     * ESC p m t1 t2
     *
     * @return bytes for this command
     */
    class func  drawerKick() -> [UInt8] {
        var result = [UInt8](count: 5, repeatedValue: 0)
        result[0] = ESC
        result[1] = 112
        result[2] = 0
        result[3] = 60
        result[4] = 120
        return result
    }
    
    
    /**
     * 选择打印颜色1（不一定有效）
     * ESC r n
     *
     * @return bytes for this command
     */
    class func  selectColor1() -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = ESC
        result[1] = 114
        result[2] = 0
        return result
    }
    
    /**
     * 选择打印颜色2（不一定有效）
     * ESC r n
     *
     * @return bytes for this command
     */
    class func  selectColor2() -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = ESC
        result[1] = 114
        result[2] = 1
        return result
    }
    
    
    /**
     * white printing mode on (不一定有效)
     * Turn white/black reverse printing mode on
     * GS B n
     *
     * @return bytes for this command
     */
    class func  whitePrintingOn() -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = GS
        result[1] = 66
        result[2] = UInt8(128)
        return result
    }
    
    /**
     * white printing mode off (不一定有效)
     * Turn white/black reverse printing mode off
     * GS B n
     *
     * @return bytes for this command
     */
    class func  whitePrintingOff() -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = GS
        result[1] = 66
        result[2] = 0
        return result
    }
    
    
    /**
     * select bar code height
     * Select the height of the bar code as n dots
     * default dots = 162
     *
     * @param dots ( heigth of the bar code )
     * @return bytes for this command
     */
    class func  barcode_height(dots: UInt8) -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = GS
        result[1] = 104
        result[2] = dots
        return result
    }
    
    /**
     * select font hri
     * Selects a font for the Human Readable Interpretation (HRI) characters when printing a barcode, using n as follows:
     *
     * @param n Font
     *          0, 48 Font A
     *          1, 49 Font B
     * @return bytes for this command
     */
    class func  select_font_hri(n: UInt8) -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = GS
        result[1] = 102
        result[2] = n
        return result
    }
    
    /**
     * select position_hri
     * Selects the print position of Human Readable Interpretation (HRI) characters when printing a barcode, using n as follows:
     *
     * @param n Print position
     *          0, 48 Not printed
     *          1, 49 Above the barcode
     *          2, 50 Below the barcode
     *          3, 51 Both above and below the barcode
     * @return bytes for this command
     */
    class func  select_position_hri(n: UInt8) -> [UInt8] {
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = GS
        result[1] = 72
        result[2] = n
        return result
    }
    
    /**
     * print bar code
     *
     * @param barcode_typ   ( Barcode.CODE39, Barcode.EAN8 ,...)
     * @param barcode2print
     * @return bytes for this command
     */
     /*
     class func  print_bar_code(barcode_typ: UInt8, barcode2print: String) -> [UInt8] {
     
     let barcodebytes = Array(barcode2print.utf8)
     //var result = new byte[3 + barcodebytes.length + 1]
     var result = [UInt8](count: 3 + barcodebytes.count + 1, repeatedValue: 0)
     result[0] = GS
     result[1] = 107
     result[2] = barcode_typ
     var idx: UInt = 3
     
     for i in  1 ... barcodebytes.count {
     result[idx] = barcodebytes[i-1]
     idx = idx + 1
     }
     result[idx] = 0
     
     return result
     }
     */
     
     /**
     * Set horizontal tab positions
     *
     * @param col ( coulumn )
     * @return bytes for this command
     */
    class func  set_HT_position(col: UInt8) -> [UInt8] {
        var result = [UInt8](count: 4, repeatedValue: 0)
        result[0] = ESC
        result[1] = 68
        result[2] = col
        result[3] = 0
        return result
    }
    
    /**
     * 字体变大为标准的n倍
     *
     * @param num 倍数
     * @return bytes for this command
     */
    class func  fontSizeSetBig(num: UInt) -> [UInt8] {
        var realSize: UInt8 = 0
        switch (num) {
        case 0:
            realSize = 0
            break
        case 1:
            realSize = 17
            break
        case 2:
            realSize = 34
            break
        case 3:
            realSize = 51
            break
        case 4:
            realSize = 68
            break
        case 5:
            realSize = 85
            break
        case 6:
            realSize = 102
            break
        case 7:
            realSize = 119
            break
        default: break
        }
        var result = [UInt8](count: 3, repeatedValue: 0)
        result[0] = GS
        result[1] = 33
        result[2] = realSize
        return result
    }
    
    /**
     * 进纸切割
     * Feeds paper to ( cutting position + n x vertical motion unit )
     * and executes a full cut ( cuts the paper completely )
     *
     * @return bytes for this command
     */
    class func  feedPaperCut() -> [UInt8] {
        var result = [UInt8](count: 4, repeatedValue: 0)
        result[0] = GS
        result[1] = 86
        result[2] = 65
        result[3] = 0
        return result
    }
    
    /**
     * 进纸切割（留部分）
     * Feeds paper to ( cutting position + n x vertical motion unit )
     * and executes a partial cut ( one point left uncut )
     *
     * @return bytes for this command
     */
    class func  feedPaperCutPartial() -> [UInt8] {
        var result = [UInt8](count: 4, repeatedValue: 0)
        result[0] = GS
        result[1] = 86
        result[2] = 66
        result[3] = 0
        return result
    }
    
    /**
     * 打印图片
     * file: 图片文件名
     */
    class func  decodeBitmap(file: String)  -> [UInt8] {
        let bmpImg: UIImage = UIImage(named: file)!
        let bmp : CGImageRef = bmpImg.CGImage!
        let bmpWidth: Int = CGImageGetWidth(bmp)
        let bmpHeight: Int = CGImageGetHeight(bmp)
        var list: [String] = [String]()
        var sb: String = ""
        //print("width: " + String(bmpWidth) + ", height: " + String(bmpHeight))
       
        // 每行字节数(除以8，不足补0)
        let zeroCount = bmpWidth % 8
        // 每行需要补充的0
        var zeroStr = ""
        if (zeroCount > 0) {
            for var i = 0; i < (8 - zeroCount); ++i {
                zeroStr = zeroStr + "0"
            }
        }
        let bmpData: CFDataRef = CGDataProviderCopyData(CGImageGetDataProvider(bmp))!
        let pointer: UnsafePointer<UInt8> = CFDataGetBytePtr(bmpData)
        
        // 逐个读取像素颜色，将非白色改为黑色
        for var i = 0; i < bmpHeight; ++i {
            sb = ""
            for var j = 0; j < bmpWidth; ++j {
                // (y * width + x) * 4
                // y = i, x = j
                let index = (i * bmpWidth + j) * 4;
                
                //var color = pointer[index] // 获得Bitmap 图片中每一个点的color颜色值
                //颜色值的R G B
                let r = pointer[index]
                let g = pointer[index + 1]
                let b = pointer[index + 2]
    
                // if color close to white，bit='0', else bit='1'
                if (r > 160 && g > 160 && b > 160) {
                    sb = sb + "0"
                } else {
                    sb = sb + "1"
                }
            }
            // 每一行结束时，补充剩余的0
            if (zeroCount > 0) {
                sb = sb + zeroStr
            }
            //print(sb)
            list.append(sb)
        }
        // binaryStr每8位调用一次转换方法，再拼合
        let bmpHexList = ConvertUtil.binaryListToHexStringList(list)
        let commandHexString = "1D763000"
        // 宽度指令
        var widthHexString = ""
        if (bmpWidth % 8) == 0 {
            widthHexString = ConvertUtil.intToHex(bmpWidth / 8)
        }
        else {
            widthHexString = ConvertUtil.intToHex(bmpWidth / 8 + 1)
        }
        if (widthHexString.characters.count > 2) {
            print("decodeBitmap error", "宽度超出 width is too large")
            return []
        } else if (widthHexString.characters.count == 1) {
            widthHexString = "0" + widthHexString
        }
        widthHexString = widthHexString + "00"
    
        // 高度指令
        var heightHexString = ConvertUtil.intToHex(bmpHeight)
        if (heightHexString.characters.count > 2) {
            print("decodeBitmap error", "高度超出 height is too large")
            return []
        } else if (heightHexString.characters.count == 1) {
            heightHexString = "0" + heightHexString
        }
        heightHexString = heightHexString + "00"
    
        var commandList = [String]()
        commandList.append(commandHexString + widthHexString + heightHexString)
        commandList =  commandList + bmpHexList
    
        return ConvertUtil.hexList2Byte(commandList)
    }
    
    
    /**
    * 合并byte数组
    *
    * @param byteArray byte数组
    * @return 一个byte数组
    */
    /*
    class func  mergerByteArray(byte[]... byteArray) -> [UInt8] {
    
    int length = 0
    for (byte[] item : byteArray) {
    length += item.length
    }
    var result = new byte[length]
    int index = 0
    for (byte[] item : byteArray) {
    for (byte b : item) {
    result[index] = b
    index++
    }
    }
    return result
    }
    */
}